using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace airwolf.SSDP
{
	public class SSDPClient
	{
        internal const int SSDPPort = 1900;
        internal const string SSDPIp = "239.255.255.250";

        private Queue<ParseMessageCommand> _q;
        private static Dictionary<string, string> _referals = new Dictionary<string,string>();
        private const int ListenerThreadCount = 1;
        private static bool _listen = true;
        private static SSDPClient _instance;
        private Mutex _mutex = new Mutex(false);
        private int _seqID = 0;
        private int _waitMax = 1000 * 30;

        private SSDPClient()
        {
#if DEBUG
            //_waitMax = 60 * 1000 * 3;
#endif
            _q = new Queue<ParseMessageCommand>();
            Thread listenThread = new Thread(Listen);
            listenThread.Start();
        }

        public static SSDPClient Instance
        {
            get
            {
                if (_instance == null)
                    _instance = new SSDPClient();
                return _instance;
            }
        }

        public static void Listen(object state)
        {
                using (UdpClient udpClient = new UdpClient(1900))
                {
                    byte[] rec;
                    SSDPMessage message;
                    IPEndPoint responseEP;
                    IPAddress ip = IPAddress.Parse(SSDPIp);

                    udpClient.JoinMulticastGroup(ip);
                    
                    IPEndPoint ep = new IPEndPoint(ip, SSDPPort);

                    while (_listen)
                    {
                        rec = udpClient.Receive(ref ep);
                        responseEP = ep;
                        string messageText = Encoding.ASCII.GetString(rec);

                        if (SSDPMessage.TryParse(messageText, out message) && _referals.ContainsKey(message.Schema))
                            Log.LogMessage("Received a {0}/{1} message from {2}", message.Type, message.Schema, ep.Address);
                        //else
                        //    Log.LogMessage("Received a message of an unknown type: {0}", messageText);

                        if (message.Schema != null && _referals.ContainsKey(message.Schema))
                            SendOK(responseEP, message.Schema, _referals[message.Schema]);
                    }
                }
        }

        private static void SendOK(IPEndPoint EP, string Schema, string URL)
        {
            string usn = String.Format("usn:{0}::{1}", Guid.NewGuid(), Schema);
            byte[] bytes = ASCIIEncoding.ASCII.GetBytes(SSDPMessage.CreateOK(usn, Schema, URL));
            //Log.LogMessage("Sending OK");
            UDP.Send(EP.Address.ToString(), EP.Port, bytes);
        }

        public static void RegisterReferal(string Schema, string URL)
        {
            _referals.Add(Schema, URL);
        }

        public static void __Notify__(string DeviceType, string uuid, string ServiceLocation)
        {
            using (UdpClient udpClient = new UdpClient())
            {
                Log.LogMessage("Connecting UDP client to {0}:{1}", SSDPIp, SSDPPort);
                
                udpClient.Connect(SSDPIp, SSDPPort);
                string send = "";// SSDPNotification.Parse(DeviceType, uuid, ServiceLocation);
                byte[] sendBytes = Encoding.ASCII.GetBytes(send);
                Log.LogMessage("Sending SSDP data: {0}", send);
                udpClient.Send(sendBytes, sendBytes.Length);
                udpClient.Close();
            }
        }

        public static void Notify(string DeviceType, string uuid, string ServiceLocation)
        {
            return;
            //Socket udpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

            using (UdpClient udpClient = new UdpClient(SSDPPort))
            {
                IPAddress ip = IPAddress.Parse(SSDPIp);
                udpClient.JoinMulticastGroup(ip);
                //Log.LogMessage("SSDP joined multicast group and is waiting to respond.");
                IPEndPoint ep = new IPEndPoint(ip, SSDPPort);
                //byte[] notifyBytes = Encoding.ASCII.GetBytes(SSDPNotification.Parse(DeviceType, uuid, ServiceLocation)); 
                //udpClient.Send(notifyBytes, notifyBytes.Length);

                //Log.LogMessage("Send a NOTIFY message");
            }
        }
	}

    /*public struct Referal
    {
        public string Schema;
        public string URL;

        public Referal(string Schema, string URL)
        {
            this.Schema = Schema;
            this.URL = URL;
        }
    }*/
}